package cz.cacek.ebook;

import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;

/**
 * Main display. On this canvas is displayed current part of book.
 *
 * @author Tom� Darmovzal [tomas.darmovzal (at) seznam.cz]
 * @author Josef Cacek [josef.cacek (at) gmail.com]
 * @author Jiri Bartos
 * @author $Author: kwart $
 * @version $Revision: 1.26 $
 * @created $Date: 2007/08/31 11:54:27 $
 */
public class EBookCanvas extends Canvas implements CommandListener {

	private List list;

	private EBookMIDlet midlet;
	private ContentView view;
	private String message = null;
	private Font messageFont;
	private Command cmdLibrary;
	private Command cmdInfo;
	private Command cmdOptions;
	private Command cmdPosition;
	private Command cmdExit;
	private Command cmdChapters;

	private Command cmdOK;
	private Command cmdCancel;

	private Gauge gauge;
	private TextField textfield;
	private Form form;

	protected final ScrollThread scrollThread = new ScrollThread();

	private Vector listVector;
	//private  int scrollDelay = Utils.AUTOSCROLL_PAUSE;

	private byte screenIdx;
	private Display display;

	/**
	 * Thread which provides autoscroll functionality.
	 *
	 * @author Josef Cacek
	 */
	class ScrollThread extends Thread {
		boolean run = false;

		public void run() {
			try {
				while (true) {
					if (canRun()) {
						if (!view.fwdLine()) {
							setRun(false);
						}
						messageOff();
					}
					Thread.sleep(getScrollDelay());
				}
			} catch (Exception e) {
			}
		}

		public synchronized void setRun(final boolean aRun) {
			run = aRun;
			BackLight backLight = EBookCanvas.this.getSetting().getBackLight();
			if (run) {
				backLight.autoscroll();
			} else {
				backLight.normal();
				messageOff();
			}
		}

		public synchronized boolean canRun() {
			return run;
		}
	}

	public SettingBean getSetting() {
		return this.midlet.getSetting();
	}

	/**
	 * Constructor
	 *
	 * @param aMidlet
	 * @throws Exception
	 */
	public EBookCanvas(EBookMIDlet aMidlet) throws Exception {
		midlet = aMidlet;
		display = Display.getDisplay(midlet);

		SettingBean setting = aMidlet.getSetting();
		//this.scrollDelay = setting.getScrollDelay();

		view = new ContentView(setting, getWidth(), getHeight());
		messageFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,
				Font.SIZE_SMALL);
		cmdChapters = new Command(ResourceProviderME.get("cmd.chapters"),
				Command.SCREEN, 1);
		cmdPosition = new Command(ResourceProviderME.get("cmd.position"),
				Command.SCREEN, 2);
		cmdOptions = new Command(ResourceProviderME.get("cmd.options"),
				Command.SCREEN, 3);
		cmdInfo = new Command(ResourceProviderME.get("cmd.info"),
				Command.SCREEN, 4);
		cmdExit = new Command(ResourceProviderME.get("cmd.exit"),
				Command.SCREEN, 10);

		cmdOK = new Command(ResourceProviderME.get("btn.ok"), Command.OK, 1);
		cmdCancel = new Command(ResourceProviderME.get("btn.cancel"),
				Command.CANCEL, 1);
		addCommand(cmdPosition);
		addCommand(cmdInfo);
		addCommand(cmdOptions);
		addCommand(cmdExit);
		if (this.getLibrary().getBookCount() > 1) {
			cmdLibrary = new Command(ResourceProviderME.get("cmd.choose"),
					Command.SCREEN, 4);
			addCommand(cmdLibrary);
		}
		setCommandListener(this);
		scrollThread.start();
	}

	/**
	 * Choosing last used book from library.
	 */
	public void begin(final long aTime) {
		Utils.debug("EBookCanvas.begin() started");
		final Library tmpLib = getLibrary();
		if (tmpLib.getBookCount() == 1) {
			tmpLib.setBookIdx(0);
		}
		final int tmpIdx = tmpLib.getBookIdx();
		if (tmpIdx > -1 && tmpLib.getBookCount() > tmpIdx) {
			selectBook(tmpLib.getBookIdx(), aTime);
		} else {
			createBookList(aTime); // new EBookBookList(this, view);
		}
		getSetting().getBackLight().normal();
		if (Utils.DEBUG) {
			Utils.debug("EBookCanvas.begin() finished");
		}
	}

	private Library getLibrary() {
		return this.midlet.getLibrary();
	}

	/**
	 * Creates and displays list with books present in this application.
	 *
	 * @return Displayable
	 */
	private Displayable createBookList(final long aTime) {
		list = new List(ResourceProviderME.get("bl.choose"), Choice.IMPLICIT);
		final Library tmpLib = getLibrary();
		for (int i = 0, n = tmpLib.getBookCount(); i < n; i++) {
			list.append(tmpLib.getBookName(i), null);
		}
		final int tmpIdx = tmpLib.getBookIdx();
		list.setSelectedIndex(tmpIdx > -1 ? tmpIdx : 0, true);
		list.setCommandListener(this);
		screenIdx = Utils.SCREEN_BOOK_LIST;
		waitForIntroEnd(aTime);
		display.setCurrent(list);
		return list;
	}

	private void waitForIntroEnd(long aTime) {
		if (aTime > 0) {
			aTime = EBookIntro.MIN_MILIS_DISPLAY + aTime
					- System.currentTimeMillis();
			if (aTime > 0) {
				try {
					Thread.sleep(aTime);
				} catch (InterruptedException e) {
					Utils.error("Thread.sleep() problem: " + e.getMessage());
				}
			}
		}
	}

	/**
	 * Creates book-info form
	 *
	 * @return Displayable
	 */
	private Displayable createBookInfo(String aName, String aDesc) {
		form = new Form(ResourceProviderME.get("i.head"));
		if (aName == null || aName.length() == 0) {
			aName = ResourceProviderME.get("i.empty");
		}
		if (aDesc == null || aDesc.length() == 0) {
			aDesc = ResourceProviderME.get("i.empty");
		}
		form.append(new StringItem(ResourceProviderME.get("i.bk.name"), aName));
		form.append("\n");
		form.append(new StringItem(ResourceProviderME.get("i.bk.desc"), aDesc));
		form.append("\n");
		form.append(new StringItem(ResourceProviderME.get("i.copy.h"),
				ResourceProviderME.get("i.copy")));
		form.append("\n");
		form.append(new StringItem(ResourceProviderME.get("i.version"),
				Utils.VERSION));
		form.append("\n");
		form.append(new StringItem(ResourceProviderME.get("i.help.h"),
				ResourceProviderME.get("i.help")));
		// fix for Siemens phones scrolling
		form.append(new StringItem("", ""));
		form.addCommand(cmdOK);
		form.setCommandListener(this);
		screenIdx = Utils.SCREEN_BOOK_INFO;
		display.setCurrent(form);
		return form;
	}

	private Book getBook() {
		return this.view.getBook();
	}

	/**
	 * Creates and displays list of Chapters
	 *
	 * @return Displayable
	 */
	private Displayable createChaptersList() {
		list = new List(ResourceProviderME.get("chap.head"), Choice.IMPLICIT);
		listVector = new Vector();
		getBook().getChapters(list, listVector);
		list.setCommandListener(this);
		screenIdx = Utils.SCREEN_CHAPTERS_LIST;
		display.setCurrent(list);
		return list;
	}

	/**
	 * Constructs gauge position form.
	 */
	private void createPositionForm() {
		form = new Form(ResourceProviderME.get("pos.head"));
		if (getSetting().isPositionAsPerc()) {
			final int tmpValue = view.getPercPosition();
			if (tmpValue < 0 || tmpValue > 100) {
				throw new RuntimeException(
						"Wrong parameters for Gauge constructor");
			}
			gauge = new Gauge(ResourceProviderME.get("pos.actual"), true, 100,
					0);
			gauge.setValue(tmpValue);
			form.append(gauge);
		} else {
			final String tmpValue = Integer.toString(view.getPosition());
			final int tmpMaxLen = Integer.toString(getBook().size).length();
			textfield = new TextField(ResourceProviderME.get("pos.actual"),
					tmpValue, tmpMaxLen, TextField.NUMERIC);
			form.append(textfield);
		}
		form.addCommand(cmdOK);
		form.addCommand(cmdCancel);
		form.setCommandListener(this);
		display.setCurrent(form);
		screenIdx = Utils.SCREEN_POSITION;
	}

	private void releaseForm() {
		form = null;
		textfield = null;
		gauge = null;
		setBookScreen();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.microedition.lcdui.Displayable#paint(javax.microedition.lcdui.Graphics)
	 */
	public void paint(Graphics g) {
		Utils.debug("EBookCanvas.paint(Graphics g)");
		view.draw(g, 0, 0);
		if (message != null) {
			int mx = 2;
			int my = 2;
			g.setFont(messageFont);
			int w = messageFont.stringWidth(message);
			int h = messageFont.getHeight();
			g.setColor(0xFFFFFF);
			g.fillRect(mx, my, w + 3, h + 3);
			g.setColor(0x000000);
			g.drawString(message, mx + 2, my + 2, Graphics.LEFT | Graphics.TOP);
			g.drawRect(mx, my, w + 3, h + 3);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.microedition.lcdui.Displayable#keyPressed(int)
	 */
	public void keyPressed(int aKey) {
		Utils.debug("Key pressed " + aKey);
		key(aKey);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.microedition.lcdui.Displayable#keyRepeated(int)
	 */
	public void keyRepeated(int aKey) {
		Utils.debug("Key repeated " + aKey);
		key(aKey);
	}

	/**
	 * Key actions handler
	 *
	 * @param aKey
	 */
	protected synchronized void key(final int aKey) {
		final int action = getGameAction(aKey);
		Utils.debug("Key: " + aKey + ", Action: " + action);
		if (!scrollThread.canRun()) {
			keyNormal(aKey, action);
		} else {
			keyAutoRun(aKey, action);
		}
		Utils.debug("Key action finished.");
	}

	/**
	 * Key actions handler
	 *
	 * @param aKey
	 */
	protected void keyNormal(final int aKey, final int action) {
		switch (action) {
		case UP:
			prevPage();
			break;
		case DOWN:
			nextPage();
			break;
		case RIGHT:
			nextLine();
			break;
		case LEFT:
			prevLine();
			break;
		default:
			switch (aKey) {
			case KEY_NUM1:
			case KEY_NUM2:
			case KEY_NUM3:
			case -13:
				prevPage();
				break;
			case KEY_NUM4:
				prevLine();
				break;
			case KEY_NUM7:
			case KEY_NUM8:
			case KEY_NUM9:
			case KEY_NUM0:
			case -14:
				nextPage();
				break;
			case KEY_NUM6:
				nextLine();
				break;
			case KEY_NUM5:
				scrollThread.setRun(true);
				break;
			}
		}
	}

	/**
	 * Key handlers for Autorun book reading.
	 *
	 * @param aKey
	 */
	protected void keyAutoRun(final int aKey, final int action) {
		if (action == UP || action == LEFT || aKey == KEY_NUM4) {
			addScrollDelay(Utils.AUTOSCROLL_STEP);
		} else if (action == DOWN || action == RIGHT || aKey == KEY_NUM6) {
			addScrollDelay(-Utils.AUTOSCROLL_STEP);
		}
		if (aKey == KEY_NUM5) {
			messageOn(ResourceProviderME.get("i.stop"));
			scrollThread.setRun(false);
		} else {
			messageOn(ResourceProviderME.get("i.delay") + getScrollDelay());
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.microedition.lcdui.Displayable#pointerPressed(int, int)
	 */
	public void pointerPressed(int aX, int aY) {
		Utils.debug("Pointer pressed (" + aX + "," + aY + ")");
		int seg = (aY * 4) / getHeight();
		if (scrollThread != null) {
			return;
		}
		synchronized (this) {
			switch (seg) {
			case 0:
				prevPage();
				break;
			case 1:
				prevLine();
				break;
			case 2:
				nextLine();
				break;
			case 3:
				nextPage();
				break;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
	 *      javax.microedition.lcdui.Displayable)
	 */
	public void commandAction(Command aCmd, Displayable aDisp) {
		Utils.debug("Command action " + aCmd);
		switch (screenIdx) {
		case Utils.SCREEN_BOOK:
			commandActNormal(aCmd);
			break;
		case Utils.SCREEN_BOOK_LIST:
			commandActBookList();
			break;
		case Utils.SCREEN_POSITION:
			commandActPosition(aCmd);
			break;
		case Utils.SCREEN_LIGHT:
		case Utils.SCREEN_BOOK_INFO:
			releaseForm();
			break;
		case Utils.SCREEN_CHAPTERS_LIST:
			commandActChapters();
			break;
		default:
			break;
		}

	}

	private void commandActPosition(final Command aCmd) {
		Utils.debug("commandAction() for book list started");
		if (form != null && aCmd == cmdOK) {
			if (this.getSetting().isPositionAsPerc()) {
				view.setPercPosition(gauge.getValue());
			} else {
				try {
					int tmpPos = Integer.parseInt(textfield.getString());
					view.setPosition(tmpPos);
				} catch (NumberFormatException nfe) {
					Utils.error("commandActPosition: Can't convert '"
							+ textfield.getString() + "' to integer.", nfe);
				}
			}
		}
		releaseForm();
	}

	private void commandActBookList() {
		Utils.debug("commandAction() for book list started");
		if (list != null) {
			selectBook(list.getSelectedIndex(), -1L);
			list = null;
		}
	}

	private void commandActChapters() {
		Utils.debug("commandAction() for chapters list started");
		if (list != null && listVector != null) {
			final Integer tmpPos = (Integer) listVector.elementAt(list
					.getSelectedIndex());
			view.setPosition(tmpPos.intValue());
			setBookScreen();
			list = null;
		}
	}

	/**
	 * select book
	 */
	private void selectBook(final int aIdx, final long aTime) {
		final Book book = this.getLibrary().getBook(aIdx);
		if(Utils.INFO){
			Utils.info("selectBook:"+book.toString());
		}
		if (book.isChapterFile()) {
			addCommand(cmdChapters);
		} else {
			removeCommand(cmdChapters);
		}
		view.setBook(book);
		waitForIntroEnd(aTime);
		setBookScreen();
	}

	protected void setBookScreen() {
		display.setCurrent(this);
		screenIdx = Utils.SCREEN_BOOK;
	}

	private void commandActNormal(final Command aCmd) {
		if (aCmd == cmdChapters) {
			createChaptersList();
		} else if (aCmd == cmdExit) {
			midlet.destroyApp(false);
		} else if (aCmd == cmdPosition) {
			createPositionForm();
		} else if (aCmd == cmdInfo) {
			Book book = view.getBook();
			createBookInfo(book.getName(), book.description);
		} else if (aCmd == cmdOptions) {
			display.setCurrent(new SettingForm(this));
		} else if (aCmd == cmdLibrary) {
			this.getLibrary().savePosition(getViewPosition());
			createBookList(0);
		}
	}

	/**
	 * Scrolls one line ahead in current book
	 */
	protected void nextLine() {
		if(Utils.INFO){
			Utils.info(" EBookCanvas.nextLine() ...");
		}
		try {
			pauseOn();
			view.fwdLine();
			messageOff();
			if(Utils.INFO){
				Utils.info(" EBookCanvas.nextLine() done");
			}
		} catch (Exception e) {
		//	e.printStackTrace();
			if(Utils.ERROR){
				Utils.error(" nextLine() failed:"+e.getMessage(), e);
			}
		}

	}

	/**
	 * Scrolls one page ahead in current book
	 */
	protected void nextPage() {
		if(Utils.INFO){
			Utils.info(" EBookCanvas.nextPage() ...");
		}
		try {
			pauseOn();
			view.fwdPage();
			messageOff();
			if(Utils.INFO){
				Utils.info(" EBookCanvas.nextPage() done");
			}
		} catch (Exception e) {
			if(Utils.ERROR){
				Utils.error(" nextPage() failed:"+e.getMessage(), e);
			}
		}
	}

	/**
	 * Scrolls one line back in current book
	 */
	protected void prevLine() {
		if(Utils.INFO){
			Utils.info(" EBookCanvas.prevLine() ...");
		}
		try {
			pauseOn();
			view.bckLine();
			messageOff();
			if(Utils.INFO){
				Utils.info(" EBookCanvas.prevLine() done");
			}
		} catch (Exception e) {
			if(Utils.ERROR){
				Utils.error(" prevLine() failed:"+e.getMessage(), e);
			}
		}
	}

	/**
	 * Scrolls one page back in current book
	 */
	protected void prevPage() {
		if(Utils.INFO){
			Utils.info(" EBookCanvas.prevPage() ...");
		}
		try {
			pauseOn();
			view.bckPage();
			messageOff();

			if(Utils.INFO){
				Utils.info(" EBookCanvas.prevPage() done");
			}
		} catch (Exception e) {
			if(Utils.ERROR){
				Utils.error(" prevPage() failed:"+e.getMessage(), e);
			}
		}
	}

	/**
	 * Displays "Wait" message
	 */
	protected void pauseOn() {
		messageOn(ResourceProviderME.get("i.wait"));
	}

	/**
	 * Displays system message (e.g. Wait) display
	 */
	protected synchronized void messageOn(final String aMsg) {
		if (getSetting().isDisplayStatus()) {
			message = aMsg;
			repaint();
			serviceRepaints();
		}
	}

	/**
	 * Disable system message (e.g. Wait) display
	 */
	protected synchronized void messageOff() {
		message = null;
		repaint();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.microedition.lcdui.Displayable#hideNotify()
	 */
	protected void hideNotify() {
		if (Utils.DEBUG) {
			Utils.debug("EBookCanvas.hideNotify()");
		}
		scrollThread.setRun(false);
	}

	/**
	 * Sets pause between scrolling.
	 *
	 * @param aDelay
	 */
	private  synchronized void setScrollDelay(int aDelay) {
		if (aDelay < Utils.AUTOSCROLL_STEP) {
			aDelay = Utils.AUTOSCROLL_STEP;
		}
		//scrollDelay = aDelay;
		this.getSetting().setScrollDelay(aDelay);
	}

	private  synchronized void addScrollDelay(int aDelay) {
		setScrollDelay(getScrollDelay() + aDelay);
	}

	/**
	 * Returns position of view.
	 *
	 * @see ContentView#getPosition()
	 * @return position of view
	 */
	public int getViewPosition() {
		return view.getPosition();
	}

	/**
	 * @return Returns the scrollDelay.
	 */
	private  synchronized int getScrollDelay() {
		return this.getSetting().getScrollDelay();
	}

	public boolean isFullscreen() {
		return this.getSetting().isFullscreen();
	}

	public void setFullscreen(boolean fullscreen) {
		// this.fullscreen = fullscreen;
		// MIDP-2.0
		// setFullScreenMode(fullscreen);
		this.getSetting().setFullscreen(fullscreen);
		view.setDimension(getWidth(), getHeight());
	}

	public boolean isDisplayStatus() {
		return this.getSetting().isDisplayStatus();
	}

	// public void setDisplayStatus(boolean displayStatus) {
	// this.getSetting().setDisplayStatus(displayStatus);
	// }

	public boolean isPositionAsPerc() {
		return this.getSetting().isPositionAsPerc();
	}

	public ContentView getView() {
		return view;
	}

	// public void setPositionAsPerc(boolean positionAsPerc) {
	// this.getSetting().setPositionAsPerc(positionAsPerc);
	// }

}
